/*
 * WeatherUserModelImpl
 * 
 * Copyright 2008-2011 Centro Andaluz de Innovacion y Tecnologias de la
 * Informacion y las Comunicaciones (CITIC). All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 *
 * Please contact CITIC - Edificio CITIC, C/ Marie Curie, 6 Parque
 * Tecnologico de Andalucía 29590 - Campanillas, Malaga, Spain
 * or visit www.citic.es, if you need additional information or
 * have any questions.
 */

package es.citic.puedes.weatherservice.impl.umm;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import javax.persistence.EntityManager;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import es.citic.puedes.weatherservice.WeatherException;
import es.citic.puedes.weatherservice.impl.bbdd.WeatherDataBase;
import es.citic.puedes.weatherservice.impl.tables.WeatherLocation;
import es.citic.puedes.weatherservice.impl.tables.WeatherUserModel;
import es.citic.puedes.weatherservice.model.Language;
import es.citic.puedes.weatherservice.umm.IWeatherUserModel;
import es.citic.puedes.weatherservice.umm.WeatherPreferences;
import es.citic.puedes.weatherservice.utils.Utils;

public class WeatherUserModelImpl implements IWeatherUserModel {

	public static Logger logger = LoggerFactory
			.getLogger(WeatherUserModelImpl.class);

	List<WeatherUserModel> userModel = new ArrayList<WeatherUserModel>();
	private WeatherDataBase wdb;
	private Integer userId;

	/**
	 * Constructor
	 * 
	 * @param userId
	 *            User id
	 * @throws WeatherException
	 *             If an exception occurred
	 */
	public WeatherUserModelImpl(Integer userId, WeatherDataBase wdb)
			throws WeatherException {

		logger.debug("Enter: WeatherUserModelImpl(" + userId + ")");

		this.userId = userId;
		this.wdb = wdb;

		// Select all WeatherUserModel from userId
		EntityManager em = wdb.getEntityManager();
		userModel = wdb.selectWeatherUserModelQuery(em, userId);
		wdb.closeEntityManager(em);

		logger.debug("Exit :WeatherUserModelImpl");
	}

	@Override
	public void saveUserModel() throws WeatherException {
		logger.debug("Enter: saveUserModel()");

		if (userModel != null) {
			EntityManager em = null;
			em = wdb.getEntityManager();
			em.getTransaction().begin();

			// Save each WeatherUserModel form user in BBDD
			for (int i = 0; i < userModel.size(); i++) {
				em.merge(userModel.get(i));
			}
			em.getTransaction().commit();
			wdb.closeEntityManager(em);
		}

		logger.debug("Exit : saveUserModel()");
	}

	@Override
	public List<WeatherPreferences> getPreferences(Language language)
			throws WeatherException {

		logger.debug("Enter: getPreferences()");

		List<WeatherUserModel> userModelList = null;
		List<WeatherPreferences> preferences = new ArrayList<WeatherPreferences>();

		EntityManager em = wdb.getEntityManager();

		// Select all WeatherUserModel from userId
		userModelList = wdb.selectWeatherUserModelQuery(em, userId);
		if (userModelList != null) {

			// Set each WeatherUserModel element in a WeatherPreferences and add
			// them in a list
			for (int i = 0; i < userModelList.size(); i++) {
				WeatherUserModel userModel = userModelList.get(i);
				WeatherLocation weatherLocation = userModel
						.getWeatherlocation();
				WeatherPreferences weatherPreferences = new WeatherPreferences(
						userId, weatherLocation.getLocation(),
						weatherLocation.getCountry());
				weatherPreferences.setValue(userModel.getValue());

				String stringDate = Utils.convertDateToStringLanguageDate(
						userModel.getDate(), language);
				weatherPreferences.setDate(stringDate);

				preferences.add(weatherPreferences);
			}
		}

		// Normalize WeatherPreferences list
		preferences = normalizeList(preferences);

		// Order WeatherPreferences list by descendant value parameter
		preferences = orderList(preferences);

		wdb.closeEntityManager(em);
		logger.debug("Exit : getPreferences()");

		return preferences;
	}

	public void setPreferences(WeatherPreferences preferences) {
		logger.debug("Enter: setPreferences()");
		
		WeatherUserModel userModelFound = null;
		WeatherLocation locationDDBB = null;
		
		int pos = 0;
		EntityManager em = null;
				
		try {
			em = wdb.getEntityManager();
			
			locationDDBB = wdb.selectWeatherLocationQuery(em,
					preferences.getLocation(), preferences.getCountry());
			
			if (userModel != null) {
				
				// Search if user has this preferences yet
				for (int i = 0; i < userModel.size(); i++) {
					WeatherUserModel model = userModel.get(i);
					WeatherLocation weatherLocation = model
							.getWeatherlocation();
					if (weatherLocation.getCountry().equals(preferences.getCountry())
							&& (weatherLocation.getLocation().equals(preferences.getLocation()))) {
						userModelFound = model;
						pos = i;
					}
				}
				if (userModelFound == null) {
					userModel.add(createWeatherUserModel(locationDDBB));

				} else {
					userModelFound.setValue(userModelFound.getValue() + 1);
					userModelFound.setDate(Calendar.getInstance().getTime());
					userModel.set(pos, userModelFound);
				}
			} else {
				userModel.add(createWeatherUserModel(locationDDBB));
			}

		} 
		catch (WeatherException e) {
			logger.error(e.getMessage(), e);
			userModel.add(createWeatherUserModel(preferences.getLocation(),
					preferences.getCountry()));
		}
		finally {
			wdb.closeEntityManager(em);
		}
				
		logger.debug("Exit: setPreferences()");
	}
	
	// ***********************************************************
	// PRIVATE METHODS
	// ***********************************************************

	/**
	 * Normalize value parameter for each WeatherPreferences
	 */
	private List<WeatherPreferences> normalizeList(
			List<WeatherPreferences> preferenceList) {

		logger.debug("Enter: normalizeList()");

		List<WeatherPreferences> normalizaPreferenceList = new ArrayList<WeatherPreferences>();
		if (!preferenceList.isEmpty()) {

			int totalEntries = preferenceList.size();
			double currentPreferenceCount = 0.0;
			double currentPreferenceValue = 0.0;
			double currentTotalRestCount = 0.0;

			for (int i = 0; i < totalEntries; i++) {
				currentTotalRestCount = 0.0;
				WeatherPreferences currentPreference = preferenceList.get(i);

				// Gets the value of the current preference
				currentPreferenceCount = preferenceList.get(i).getValue();

				// Calculates the sum of the preferences
				for (int j = 0; j < totalEntries; j++) {
					currentTotalRestCount = currentTotalRestCount
							+ preferenceList.get(j).getValue();
				}

				// Calculates the normalized value for the current preference
				// using the sum of preferences
				currentTotalRestCount = Math.abs(currentTotalRestCount);
				currentPreferenceValue = currentPreferenceCount
						/ (currentTotalRestCount / 100);

				// Creates and adds the normalized preference to a set
				WeatherPreferences p = new WeatherPreferences(userId,
						currentPreference.getLocation(),
						currentPreference.getCountry());
				Double doubleValue = new Double(currentPreferenceValue);
				p.setValue(doubleValue.intValue());
				p.setDate(currentPreference.getDate());
				normalizaPreferenceList.add(p);
			}
		}

		logger.debug("Exit: normalizeList()");
		return normalizaPreferenceList;
	}

	/**
	 * Order list WeatherPreferences descendant by value
	 */
	private List<WeatherPreferences> orderList(
			List<WeatherPreferences> preferenceList) {

		logger.debug("Enter: orderList()");

		List<WeatherPreferences> orderPreferenceList = new ArrayList<WeatherPreferences>();
		if (!preferenceList.isEmpty()) {
			orderPreferenceList.add(preferenceList.get(0));
			preferenceList.remove(0);
			int i = 0;

			// Order descendant list by value parameter
			while (!preferenceList.isEmpty()) {
				WeatherPreferences preference = preferenceList.get(0);
				if (i == orderPreferenceList.size()) {
					orderPreferenceList.add(i, preference);
					preferenceList.remove(0);
				} else {
					WeatherPreferences orderPreference = orderPreferenceList
							.get(i);
					if (orderPreference.getValue() <= preference.getValue()) {
						orderPreferenceList.add(i, preference);
						preferenceList.remove(0);
						i = 0;
					} else {
						i++;
					}
				}

			}
		}

		logger.debug("Exit: orderList()");
		return orderPreferenceList;
	}
	
	/**
	 * Create a new WeatherUserModel
	 */
	private WeatherUserModel createWeatherUserModel(WeatherLocation locationDDBB) {
		logger.debug("Enter: createWeatherUserModel");

		WeatherUserModel newUserModel = new WeatherUserModel(1, locationDDBB,
				userId, Calendar.getInstance().getTime());
		logger.debug("Exit: createWeatherUserModel");
		return newUserModel;
	}

	/**
	 * Create a new WeatherUserModel
	 */
	private WeatherUserModel createWeatherUserModel(String location,
			String country) {
		logger.debug("Enter : createWeatherUserModel");
		WeatherLocation newWeatherLocation = new WeatherLocation(location,
				country);

		WeatherUserModel newUserModel = new WeatherUserModel(1,
				newWeatherLocation, userId, Calendar.getInstance().getTime());
		logger.debug("Exit : createWeatherUserModel");
		return newUserModel;
	}
}
